//==============================================================================
//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_
//                               (why all the numbers? I prefer 80-column text)
//
//      _partMain.cpp    main() function and introduction
//                       to this particle-system program;
//                       See bottom of CPartSys.h for detailed explanation
//==============================================================================
//  _partMain.h and _partMain.cpp build from OpenGL/ GLUT 'starter' code to:
//      --the GL_MODELVIEW matrix
//        used to manipulate objects ('model') and to position the camera
//        ('view'). GL_MODELVIEW is actually a push-down/pop-up stack of
//        matrices, 32 matrices deep, to let you manipulate jointed objects.
//      --the GL_PROJECTION matrix
//				that describes how the openGL 'camera' transforms 3D to 2D,
//              (or more precisely: from 3D 'camera' coords to the CVV (the
//              Canonical View Volume: +/-1,+/-1,+/-1) for clipping and display)
//
//	TRANSFORMATIONS OVERVIEW:
//		OpenGL transforms all vertices first by GL_MODELVIEW matrix and then by
//		the GL_PROJECTION matrix, (and then 'viewport') for display on screen.
//  --The GL_MODELVIEW matrix converts 'model' coord. system to the 'world'
//      coord system to the 'camera' or 'eye' or view' coord system (origin at
//      the camera's center-of-projection, +X rightwards, +Y upwards, and the
//      camera looks in the -Z direction (+Z axis pokes you in the eye).
//  --The GL_PROJECTION matrix transforms all contents of a portion of this 3D
//      'view' space. It transforms anything within the camera's viewing frustum
//      (a truncated pyramid with its apex at the origin and extending outwards
//      in the -Z direction bounded by left,right,top,bottom,near,far planes),
//      and warps that frustum to match the canonical view volume (CVV), the
//      simple 3D cube centered at the origin with sides at (+/-1) in x,y,z.
//      The CVV's x,y coordinates get transformed to screen pixels by the
//      glViewport() transform, and the CVV's z-axis values become the z-buffer
//      contents of those pixels, and its value determines 3D depth-ordering
//      for occlusion during rendering: if two OpenGL drawing primitives cover
//      the same pixel's x,y value, 'Z-buffering' will compute the pixel's color
//      from the OpenGL drawing primitive with the more-positive Z value (the
//      item nearer to the eye in 'world' coordinates).
//
//      The coordinate systems are:
//     model space --GL_MODELVIEW---> view space --GL_PROJECTION--> CVV space
//         (where 'space' means a new, transformed 'coordinate system'
//         (NOT transformed points!) in which we plot our points without
//         changing their coordinates.)
//         Note that we usually define camera positions and aiming directions
//         in 'world' space that gets defined as first contents of GL_MODELVIEW.
//
//		OpenGL hardware clips the GL_PROJECTION matrix output to the 'canonical
//		viewing volume', a cube that spans +1 to -1 in Cartesian x,y,z coords.
//		(see FS Hill, chapter 7).  If GL_PROJECTION is set properly, then the
//		the walls of this cube corresponds to the 'viewing frustum' we defined
//		for the our projective transformation; 3D points at the frustum's...
//		'Near' and 'Far'   clipping planes map to CVV's z== +1,-1 respectively,
//		'Top' and 'Bottom' clipping planes map to CVV's y== +1,-1, and
//		'Right'and 'Left'  clipping planes map to CVV's x== +1,-1.
//		In this 'canonical cube', the z direction measures 'pseudo-depth'.
//
//		Humans tend to assess the positions of *everything* in world space, both
//      the models we make and the position of the camera that views them to
//      make a picture. That's quite inconvenient for OpenGL rendering--we can
//      design much more sensible graphics programs if we define small local
//      coord systems for each 3D object, or 'model' we wish to draw, and then
//      transform these individual 'model' coord systems to the 'world' coord
//      system by a tree of adjustable transformations (recall that the 'tree'
//      lets us make easily-adjustable jointed objects).  The MODELVIEW matrix
//      stack transforms basic shapes in model space to the camera's coordinate
//      system, where our eye is at the origin and we're looking down the -z
//      axis (why not +z? to keep a right-handed coordinate system).
//      The GL_PROJECTION matrix then emulates the camera; it transforms the
//      contents of a well-defined viewing frustum (left,right,top,bottom,near,
//      far) to become the contents of the canonical viewing volume (CVV), and
//      any portion of any OpenGL drawing primitive that falls OUTSIDE that CVV
//      is clipped and discarded.  The rest is rendered on-screen.
//
//	OPERATION:
//	 1)Draws 'world-space' axes as 3 colored lines: +x=RED, +y=GREEN, +z = BLUE
//          Showing 'world-space' axes reveals where we have placed our camera
//          and where it is looking in world-space.
//		--MOUSE left-click/drag applies camera rotation on x and y axes, using
//          OpenGL's glRotate() commands on GL_MODELVIEW matrix.  Our camera
//          always looks towards the world-space origin, but we can pivot it
//          around the world-space origin to see that origin from any direction.
//   2) Draws 'model-space' axes as 3 colored lines: +x=YELLOW,+y=CYAN,+z=PURPLE
//          These are the axes show the coord system used to define the base of
//          a robot-like device; first of a series of nested coord systems.
//      --ARROW keys applies x,y glTranslate() to move the model-space axes as
//          measured from the world-coord system axes.
//		--MOUSE right-click/drag applies x,y glRotation() to the world-space
//          directions of the model-space coordinate axes
//
//		-- 'R' key to reset model-to-world matrix to initial values.
//		-- 'r' key to reset the world-to-camera matrix to its initial values.
//
//		-- 'q' or 'Q' of 'space' or 'ESC' key to quit.
//
//
//  for CS 351, Northwestern University, Jack Tumblin, jet@cs.northwestern.edu
//
//  01/21/2008 - J. Tumblin--renamed particleStart01, added lighting & matl's
//                          made first version of basic particle system, built
//                          up from CS351 starter code with CTransRot class.
//  06/20/2010 - J. Tumblin--major revisions to organize into classes: CPartSys,
//                          CPart, CForcer, CWall.
//  04/10/2012 - J. Tumblin--revise/update for new EECS351 course materials
//==============================================================================

#include "_partMain.h"

//===================
//
// GLOBAL VARIABLES (bad idea!)
//
//====================
CPartSys  mySys;            // One complete particle system


//ADJUSTABLE CAMERA SETTINGS  (call setCamera() function to apply them)
GLdouble  myFOV=20.0;       // Camera's vertical field-of-view, in degrees.
                            // use pageUP/pageDOWN to change this.
int     my_w,my_h;          // camera image width, height. Set by myReshape()
                            // to match the on-screen window size in pixels.
CTransRot setCam;			// world-space to eye-space transform
CTransRot setModel;			// robot-space to world-space transform

int main( int argc, char *argv[] )
//------------------------------------------------------------------------------
{
	my_glutSetup(&argc, argv);		// GLUT calls that define windows, register
									// callbacks, etc.
    mySys.initExample1();           // Create a cube of particles in a system...
//  mySys.initExample2();           // Make your own!
//	mySys.initExample3();  ...

	my_oglSetup();					// Then set any non-default OpenGL state.
	my_printHelp();                 // Tell users what keys do what,
	glutMainLoop();
	// Then give program control to GLUT.  This is an infinite loop, and from
	// within it GLUT will call the 'callback' functions below as needed.
	return 0;							// orderly exit.
}

void my_glutSetup(int *argc, char **argv)
//------------------------------------------------------------------------------
// A handy place to put all the GLUT library initial settings; note that we
// 'registered' all the function names for the callbacks we want GLUT to use.
{
	glutInit(argc, argv);				// GLUT's own internal initializations.
							// double buffered display,
							//  RGB color model, use Z-buffering (depth buffer)
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(NU_WIDTH, NU_HEIGHT);	// set display-window size
	glutInitWindowPosition(NU_XPOS, NU_XPOS);	// and position,
	glutCreateWindow(NU_TITLE);					// then create it.

	// Register GLUT Callback function names. (these names aren't very creative)
	glutDisplayFunc(myDisplay);			// 'display'  callback:  myDisplay();
	glutReshapeFunc(myReshape);			// 'reshape'  callback:  myReshape();
	glutKeyboardFunc(myKeyboard);		// 'keyboard' callback:  myKeyboard();
	glutSpecialFunc(myKeySpecial);		// 'special'keys callback: myKeySpecial()
	glutMouseFunc(myMouseClik);			// callbacks for mouse click, move
	glutMotionFunc(myMouseMove);
}

void my_oglSetup()
//------------------------------------------------------------------------------
// A handy place to put all the OpenGL initial settings-- remember, you only
// have to change things if you don't like openGL's default settings.
{
	glClearColor(0.0, 0.0, 0.0, 0.0);	// Display-screen-clearing color;

										// acts as the 'background color'
	glColor3f(0.0, 0.0, 0.0);			// Select current color  for drawing
//	glShadeModel(GL_FLAT);				// Choose 'flat shading' model
	glShadeModel(GL_SMOOTH);			// or 'smooth' shading model (default)
//	glDisable(GL_LIGHTING);				// No lighting needed (default)
	glEnable( GL_DEPTH_TEST );			// enable hidden surface removal
}

void my_printHelp(void)
//------------------------------------------------------------------------------
{
    cout << "\n Particle System Starter Code \n" << endl;
    cout << "================================================================\n" << endl;
    cout << "p,P keys        -- toggle run/stop  \n" << endl;
    cout << "SPACE bar       -- single-step (p key to run again)\n" << endl;
    cout << "\n";

    cout << "0,1,2,...9 keys -- toggle numbered force-maker on/off\n" << endl;
    cout << "g,G keys        -- toggle Earth gravity on/off\n" << endl;
    cout << "d,D keys        -- toggle viscous drag on/off\n" << endl;
    cout << "b,B keys        -- toggle bubble force on/off\n" << endl;
    cout << "n,N keys        -- select, restart particle system\n" << endl;
    cout << "r,R keys        -- Reset Model, Camera matrices \n" << endl;
    cout << "s,S keys        -- change solver --Euler,Implicit,Midpoint,...\n" << endl;
    cout << "\n" << endl;
    cout << "s,S keys        -- change solver --Euler,Implicit,Midpoint,...\n" << endl;
    cout << "\n" << endl;

    cout << "left mouse drag -- Rotate camera\n" << endl;
    cout << "rt.  mouse drag -- Rotate Model\n" << endl;
    cout << "arrow keys      -- Translate Model coord system \n" << endl;
    cout << "PgUp/PgDown keys-- Zoom Camera in/out\n" << endl;
    cout << "q,Q,ESC keys    -- Quit\n" << endl;
}

void setCamera(void)
//------------------------------------------------------------------------------
// Simple camera-setting function, uses the global myFOV to permit zoom adjust.
// to change zoom, adjust myFOV and then call setCamera().
{
	glMatrixMode(GL_PROJECTION);		// Select the Projection matrix,
	// Set this matrix to define the camera's 'intrinsic' (internal) params

//***CHOOSE A CAMERA:***
//  CAMERA 4 -----------------------------------------------
	glLoadIdentity();			// (Clear out any previous camera settings)
	gluPerspective(				// Set camera's internal parameters:
		myFOV,					// vertical (y-axis) field-of-view in degrees,
		(double)my_w/(double)my_h,	// display image aspect ratio (width/height),
		NU_ZNEAR,NU_ZFAR);		// near,far clipping planes for camera depth.
        // for camera positioning, search for gluLookAt() function.
}

void myReshape(int w, int h)
//------------------------------------------------------------------------------
// GLUT 'reshape' Callback. Called when user resizes the window from its current
// shape to one with width w, height h.
// We usually initialize (or re-initialize)openGL's 'GL_PROJECTION' matrix here.
{
	// set size of viewport to window size.
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	my_w = w;
	my_h = h;       // set global vars for setCamera;
    setCamera();    // set GL_PROJECTION matrix and related values.
//-----------------END cameras.

	// Re-initialize the modelview matrix too!
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();					// Set it to 'do nothing'.
	glutPostRedisplay();				// Re-draw the window (needed for the
										// first drawing, when window 1st opens)
}

void myDisplay(void)
//------------------------------------------------------------------------------
// GLUT 'display' Callback.  GLUT calls this fcn when it needs you to redraw
// the display window's contents.  Your program should never call 'myDisplay()',
// because it will confuse GLUT--instead, call glutPostRedisplay() if you need
// to trigger a redrawing of the screen.
{
	// Clear the frame-buffer and the Z-buffer; ready for a new drawing.
    // COOL: what happens when you comment out this line?  Do you know why?
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
									// clear the color and depth buffers
// =============================================================================
// START DRAWING CODE HERE
// =============================================================================
	glMatrixMode(GL_MODELVIEW);		// select the modelview matrix,

	glLoadIdentity();			// wipe out current GL_MODELVIEW matrix so that
								// coordinate systems are same for model-space,
                                // world-space, and cam-space.
//=============GL_MODELVIEW matrix is now: [I]  ( the Identity matrix)
	//      (openGL will change each vertex V to  [I]V before drawing it)
	//      Any drawing commands we send now will draw vertices specified in the
    //      cam-space coord. system, fixed to our camera lens. =================

    // make an cam-space-to-world-space transform matrix:
    // Start with CAMERA coord system; make a copy, transform it by view matrix
    // to make the world coord system.
    // The gluLookAt() fcn is just one of many good ways to make a view matrix:
	gluLookAt(-5.0, 2.0, 8.66,	// VRP: eyepoint x,y,z position in world space.
			   0.0, 0.0, 0.0,	// 'look-at' point--we're looking at origin.
								// (VPN = look-at-point - VRP)
			   0.0, 1.0, 0.0);	// VUP: view 'up' vector; set 'y' as up...
	setCam.applyMatrix();	    // then transform by setCams (from mouse, etc)
                                // & call the result the 'world' coord system.

//=============GL_MODELVIEW matrix is now [I][setCam], or just [setCam].
    //      Any drawing commands we send now will draw vertices specified in
    //      world-space coords (not eye-space); fixed to the earth.=============

	// Draw world-space axes:
    glDisable(GL_LIGHTING);     // (turn OFF lighting to get simple colors)
	glBegin(GL_LINES);			// start drawing lines:
		glColor3f ( 1.0, 0.0, 0.0);	// Red X axis
		glVertex3f( 0.0, 0.0, 0.0);
		glVertex3f( 1.0, 0.0, 0.0);

		glColor3f ( 0.0, 1.0, 0.0);	// Green Y axis
		glVertex3f( 0.0, 0.0, 0.0);
		glVertex3f( 0.0, 1.0, 0.0);

		glColor3f ( 0.0, 0.0, 1.0);	// Blue Z axis
		glVertex3f( 0.0, 0.0, 0.0);
		glVertex3f( 0.0, 0.0, 1.0);
	glEnd();					// end drawing lines
	glEnable(GL_LIGHTING);     // (restore lighting)

  	// ***IN WORLD SPACE***  Now set up an OpenGL light source 0
	//=---------------------------------------------------
	// CONSIDER making a C++ class for an OpenGL light to organize its data...
	GLfloat myLamp_ambient[]  = { 0.2f, 0.2f, 0.2f, 1.0f };
    GLfloat myLamp_diffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
//    GLfloat myLamp_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    GLfloat myLamp_position[] = {-2.0f, 1.0f, 0.0f, 0.0f };
	// Set openGL's light source 0 values
	glLightfv (GL_LIGHT0, GL_AMBIENT, myLamp_ambient);
    glLightfv (GL_LIGHT0, GL_DIFFUSE, myLamp_diffuse);
    glLightfv (GL_LIGHT0, GL_POSITION,myLamp_position);

    glEnable (GL_LIGHTING);		// enable OpenGL lighting itself, and
    glEnable (GL_LIGHT0);		// enable the light source we adjusted.
    							// (lights stay off until we enable them)
	//--------------------------------------------------------

    glPushMatrix();  // SAVE the matrix that lets us draw in world space coords.

    // Second, we apply the world-to-model-space transform matrix:
        setModel.applyMatrix(); // apply robot-positioning transform that
	                            // we collected from mouse and keyboard input.
//=============GL_MODELVIEW matrix stack is now:
    //  [setCam][setModel]; (draw in model-space)
    //  [setCam]; (draw in world-space)
    //      Any drawing commands we send now will draw vertices specified in
    //      model-space coord system; (e.g. the base of our model)============
    //
    // (*PLEASE NOTE* openGL's seemingly 'backwards' application of matrices;
    //  our code puts in 'setCam' BEFORE it puts 'setModel' into GL_MODELVIEW,
    //  but the resulting GL_MODELVIEW matrix is [setCam][setModel].
    //  This is the 'duality' we talked about in class: if we interpret the
    //  matrix as moving the COORDINATE SYSTEM AXES while the points remain
    //  attached to their original coord system at their same fixed coordinate
    //  values,(as assumed in openGL API), then we should apply setCam first,
    //  then setModel, as we did in the code.  OR, FULLY EQUIVALENTLY, we can
    //  interpret the matrix as a device that doesn't move vertices or
    //  coordinate systems, but instead changes the vertex coord values from
    //  one coordinate system to another (as implemented by vertex pipeline
    //  hardware) then we begin with vertex coords in the model coord. system,
    //  transform them to their equivalent coords in world space, then to equiv
    //  coords in eye space. Each change is a matrix multiply--begin with model
    //  to world space, finish whth world-to-cam space, as shown by the contents
    //  of the GL_MODELVIEW matrix. Any vertex V is multiplied FIRST by setModel
    //  then by setCam result_v = [setCam]([setModel](v).

    // Draw model-space axes:
        glDisable(GL_LIGHTING);     // (turn OFF lighting to get simple colors)
        glBegin(GL_LINES);
            glColor3f ( 1.0, 1.0, 0.0);	// Yellow X axis
            glVertex3f( 0.0, 0.0, 0.0);
            glVertex3f( 1.0, 0.0, 0.0);

            glColor3f ( 0.0, 1.0, 1.0);	// Cyan Y axis
            glVertex3f( 0.0, 0.0, 0.0);
            glVertex3f( 0.0, 1.0, 0.0);

            glColor3f ( 1.0, 0.0, 1.0);	// Purple Z axis
            glVertex3f( 0.0, 0.0, 0.0);
            glVertex3f( 0.0, 0.0, 1.0);
        glEnd();
        glEnable(GL_LIGHTING);     // restore lighting

        mySys.drawMe();                 // Draw the particle system

    glPopMatrix();                  // go back to world-space.

	// =========================================================================
	// END DRAWING CODE HERE
	// =========================================================================

//	cout << "Screen ReDrawn" << endl;
	glutSwapBuffers();			// Double-buffering: show the newly-drawn image.

    // ANIMATION:
    // We've just finished showing our latest frame of animation. Now register
    // the myIdle() callback function in GLUT, where we compute the next frame
    // of our animation. Once we register myIdle(), it can be dangerous: GLUT
    // will call the registered myIdle() function whenever it has no other
    // tasks, and use 100% of available CPU time if myIdle() stays registered.
    //      However, at the end of myIdle(), after we've computed the particle
    // system's next frame, we UN_register it, then call glutPostRedisplay()
    // to show the new frame on-screen.
    //
    // ANIMATION with run/stop/step ability:
    // The 'P' key pauses animation; single-step using space bar.
    // This is a bit trickier: myDisplay() just finished showing the latest
    // frame of animation; if mySys.runNotStep is true, we RUN, so we just
    // register the myIdle function, as before.  However, if mySys.runNotStep is
    // FALSE, we DON'T want that new frame computed; so we DON'T register
    // myIdle() here.  Instead, we register it ONLY when user presses spacebar.
    if(true == mySys.runNotStep)
    {
        glutIdleFunc(myIdle);
    }
    // Note: as the myIdle() function un-registers itself as it finishes, you
    // re-registering this callback fcn will always advance our animation by
    // just one time-step.
}

void myKeyboard(unsigned char key, int x, int y)
//------------------------------------------------------------------------------
// GLUT 'keyboard' Callback.  User pressed an alphanumeric keyboard key.
// ('special' keys such as return, function keys, arrow keys?  myKeySpecial() ).
{
int i;
int ftype,fnum;

    i=x;    // stops warnings about unused args
    i=y;
	switch(key) {
		case 27: // Esc
		case 'Q':
		case 'q':
			exit(0);		// Quit application
			break;
		case 'b':
        case 'B':           // Toggle BUBBLE force on/off.
            for(i=0; i<mySys.forcerCount; i++)   // search all CForcer objects
            {
                if(F_BUBBLE ==  mySys.pF0[i].forceType ||
                   F_BUBBLE == -mySys.pF0[i].forceType)// found drag?
                {   // toggle: if off turn on, if on turn off.
                    mySys.pF0[i].forceType = -mySys.pF0[i].forceType;
                    if(mySys.pF0[i].forceType > 0)
                        cout << "Bubble (force [" << i << "]) ON +++." << endl;
                    else
                        cout << "Bubble (force [" << i << "]) OFF +++." << endl;
                }
            }
            break;		case 'd':
        case 'D':           // Toggle DRAG force on/off.
            for(i=0; i<mySys.forcerCount; i++)   // search all CForcer objects
            {
                if(F_DRAG ==  mySys.pF0[i].forceType ||
                   F_DRAG == -mySys.pF0[i].forceType)// found drag?
                {   // toggle: if off turn on, if on turn off.
                    mySys.pF0[i].forceType = -mySys.pF0[i].forceType;
                    if(mySys.pF0[i].forceType > 0)
                        cout << "Viscous Drag (force [" << i <<"]) ON +++. \n" << endl;
                    else
                        cout << "Viscous Drag (force [" << i <<"]) OFF +++. \n" << endl;
                }
            }
            break;
		case 'g':
        case 'G':           // Toggle Earth Gravity on/off.
            for(i=0; i<mySys.forcerCount; i++)   // search all CForcer objects
            {
                if(F_GRAV_E ==  mySys.pF0[i].forceType ||
                   F_GRAV_E == -mySys.pF0[i].forceType)     // Earth gravity?
                {   // toggle: if off turn on, if on turn off.
                    mySys.pF0[i].forceType = -mySys.pF0[i].forceType;
                    if(mySys.pF0[i].forceType > 0)
                        cout << "Earth Gravity (force [" << i << "%d]) ON +++. \n" << endl;
                    else
                        cout << "Earth Gravity (force [" << i << "%d]) ON +++. \n" << endl;
                }
            }
            break;
        case 'n':           // Restart the particle system:
			setCam.reset();	             // reset viewpoint,
			setModel.reset();
	        setModel.y_pos = 0.8;        // Move viewpoint to show bottom of
	                                     // the F_BUBBLE + F_GRAV_E force well.
            mySys.initExample1();
            cout << "\n New particle system! hit 'p' to start, or 'N' for other choice...\n" << endl;
            break;
        case 'N':           // Restart the particle system:
            mySys.initExample3();
            cout << "\n New particle system! hit 'p' to start, or 'N' for other choice...\n" << endl;
            break;
        //------------------------- Control forces:
        case '0':           // use number keys to toggle forces on/off.
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            fnum = (int)key - 48;       // ascii to integer convert
            if(fnum >= mySys.forcerCount)
            {   // Error reporting
                cout << "\n!! Force number " << fnum << "?!?! we only have ";
                cout << mySys.forcerCount-1 << "!!\n" << endl;
                break;      // DO NOTHING.
            }
            ftype = mySys.pF0[fnum].forceType;  // change forceType sign
            mySys.pF0[fnum].forceType = -ftype;
            switch(ftype)
            {       // BOTH positive (enabled) and neg(disabled) versions
                case  F_NONE:   cout << "Do-nothing force:" << endl; break;
                case  F_MOUSE:
                case -F_MOUSE:  cout << "Mouse-applied force:" << endl; break;
                case  F_GRAV_E:
                case -F_GRAV_E: cout << "Earth-gravity force:" << endl; break;
                case  F_GRAV_P:
                case -F_GRAV_P: cout << "Planetary-gravity force:" << endl; break;
                case  F_WIND:
                case -F_WIND:   cout << "Wind force:" << endl; break;
                case  F_BUBBLE:
                case -F_BUBBLE: cout << "Bubble force:" << endl; break;
                case  F_DRAG:
                case -F_DRAG:   cout << "Viscous Drag force:" << endl; break;
                case  F_SPRING:
                case -F_SPRING: cout << "Spring force:" << endl; break;
                case  F_SPRINGSET:
                case -F_SPRINGSET: cout << "Spring-set force:" << endl; break;
                case  F_CHARGE:
                case -F_CHARGE: cout << "Electrostatic charge force:" << endl; break;
                default:
                                cout << "\n ERROR: myKeyboard(): UNKNOWN FORCE TYPE!! \n\n" << endl;
                    break;
            }
            if(ftype >0)        // if original state was 'enabled', then
            {
                cout << "FORCE " << fnum <<  " TURNED OFF --- \n " << endl;
            }
            else
            {
                cout << "FORCE " << fnum << " TURNED ON +++ \n" << endl;
            }
            break;
        //------------------------
		case 'P':
        case 'p':
                                            // Pause/unpause the animation
            if(true == mySys.runNotStep)    // if true, make false,
            {
                mySys.runNotStep = false;
                cout << "\n Pause. (hit space bar to single-step)\n" << endl;
            }
            else                            // if not true, make true.
            {
                mySys.runNotStep = true;
                cout << "\n Run. (hit P to pause, space bar to single-step)\n" << endl;
            }
            break;
        case ' ':           // space bar: single step!
            if(true == mySys.runNotStep)
            {
                mySys.runNotStep = false;
                cout << "\n Pause: (hit 'P' to run, hit space bar to single-step)\n" << endl;
            }
            mySys.runNotStep = false;       // be SURE it's false...
            glutIdleFunc(myIdle);	// tricky: register the 'myIdle()' fcn;
                                    // GLUT will call it ASAP, it will compute
                                    // the next particle system step, un-register
                                    // itself, and call glutPostRedisplay() to
                                    // trigger 'myDisplay()' and show new image.
            cout << ".Step.." << endl;
            break;
		case 'r':
			setCam.reset();	// reset viewpoint,
			cout << "\nReset viewpoint.\n" << endl;
			break;
		case 'R':
			setModel.reset();	// reset modelview transformation
			cout << "\nReset modelview.\n" << endl;
			break;
		case 's':
		case 'S':
            cout << "Change solver from " << endl;
            for(i=0; i<2; i++)
            {
                cout << "%d " << mySys.solverType  << endl;
                switch(mySys.solverType)
                {
                    case NU_SOLV_EULER:      cout << "Euler " << endl;  break;
                    case NU_SOLV_MIDPOINT:   cout << "Mid point " << endl; break;
                    //case NU_ADAMS_BASH:      cout <<  "Adams-Bashforth " << endl; break;
                    //case NU_SOLV_RUNGEKUTTA: cout << "Runge-Kutta " << endl; break;
                    case NU_SOLV_IMPLICIT:   cout << "Implicit (iterative) " << endl; break;
                    case NU_SOLV_VERLET:     cout << "Vertlet " << endl; break;
                    case NU_SOLV_VEL_VERLET: cout << "Velocity-Verlet " << endl; break;
                    default:
                        cout << "\n!!ERROR! Specified unknown solver type ";
                        cout << mySys.solverType << "!!!\n" << endl;
                        break;
                }
                if(i==0)
                {
                    cout << "to " << endl;      // advance to the next solver type;
                    mySys.solverType = (mySys.solverType+1) %NU_SOLV_MAX;
                }
            }
            cout << ".\n"  << endl;             // end the line with a period.
            break;
		default:
			cout << "\n unknown key: ('" << (char)key << "' integer " << (int)key <<
                    ").  Try p,arrow keys, b,d,g,h or quit using q,Q,ESC \n" << endl;
			break;
	}
	// We might have changed something. Force a re-display
	glutPostRedisplay();
}

void myKeySpecial(int key, int x, int y)
//------------------------------------------------------------------------------
// GLUT 'special' Callback.  User pressed an non-alphanumeric keyboard key, such
// as function keys, arrow keys, etc.
{
int i;
    i=x; i=y; // stops warnings about unused args x,y
	switch(key)
	{
		case GLUT_KEY_UP:		// up arrow key
			setModel.y_pos += 0.1;
			break;
		case GLUT_KEY_DOWN:		// dn arrow key
			setModel.y_pos -= 0.1;
			break;
		case GLUT_KEY_LEFT:		// left arrow key
			setModel.x_pos += 0.1;
			break;
		case GLUT_KEY_RIGHT:	// right arrow key
			setModel.x_pos -= 0.1;
			break;
		case GLUT_KEY_PAGE_UP:    // zoom in:
            myFOV /= 1.1;           // 10% change in FOV.
            setCamera();            // call our little openGL-camera-setting fcn.
		    break;
		case GLUT_KEY_PAGE_DOWN:  // zoom out.
		    myFOV *= 1.1;
            if(myFOV > 85.0)
            {
                myFOV = 85.0;
                cout << "\nSTOPPED! Field of View can't exceed 170 degrees!\n"  << endl;
            }
            setCamera();            // call our little openGL-camera-setting fcn.
            break;
		default:
			break;
	}
//	cout << "key=" << char)key << ", " << (int)key;
//  cout << ", setModel.x_pos=" << setModel.x_pos;
//  cout << ", setModel.y_pos=" << setModel.y_pos << endl;
	glutPostRedisplay();
}

void myMouseClik(int buttonID, int upDown, int xpos, int ypos)
//------------------------------------------------------------------------------
// GLUT 'mouse' Callback.  User caused a click/unclick event with the mouse:
//     buttonID== 0 for left mouse button,
//			  (== 1 for middle mouse button?)
//			   == 2 for right mouse button;
//		upDown == 0 if mouse button was pressed down,
//			   == 1 if mouse button released.
//		xpos,ypos == position of mouse cursor, in pixel units within the window.
// *CAREFUL!* Microsoft puts origin at UPPER LEFT corner of the window.
{
	if(buttonID==0)				// if left mouse button,
	{
		if(upDown==0)			// on mouse press,
		{
			setCam.isDragging = 1;	// get set to record GL_PROJECTION changes.
			setCam.m_x = xpos;		// Dragging begins here.
			setCam.m_y = ypos;
		}
		else setCam.isDragging = 0;
	}
	else if(buttonID==2)		// if right mouse button,
	{
		if(upDown==0)
		{
			setModel.isDragging = 1;// get set to record GL_MODELVIEW changes.
			setModel.m_x = xpos;	// Dragging begins here.
			setModel.m_y = ypos;
		}
		else setModel.isDragging = 0;
	}
	else						// something else.
	{
		setCam.isDragging  = 0;	// default; DON'T change GL_PROJECTION
		setModel.isDragging = 0;	//					or  GL_MODELVIEW
	}

	cout << "clik: buttonID=" << buttonID << ", upDown=" << upDown <<
	", xpos,ypos=" << xpos << "," << ypos << endl;
}

void myMouseMove(int xpos,int ypos)
//------------------------------------------------------------------------------
// GLUT 'move' Callback.  User moved the mouse while pressing 1 or more of the
// mouse buttons.  xpos,ypos is the MS-Windows position of the mouse cursor in
// pixel units within the window.
// CAREFUL! MSoft puts origin at UPPER LEFT corner pixel of the window!
{
#define JT_INCR 1.0					// Degrees rotation per pixel of mouse move

	if(setModel.isDragging==1)			// if we're dragging the left mouse,
	{								// increment the x,y rotation amounts.
		setModel.x_rot += JT_INCR*(xpos - setModel.m_x);
		setModel.y_rot += JT_INCR*(ypos - setModel.m_y);
		setModel.m_x = xpos;		// and update current mouse position.
		setModel.m_y = ypos;
	}
	if(setCam.isDragging==1)		// if we're dragging theright mouse,
	{								// increment the x,y rotation amounts.
		setCam.x_rot += JT_INCR*(xpos - setCam.m_x);
		setCam.y_rot += JT_INCR*(ypos - setCam.m_y);
		setCam.m_x = xpos;
		setCam.m_y = ypos;
	}

	cout << "move " << xpos << "," << ypos << endl;	// print what we did.

	// We might have changed something. Force a re-display
	glutPostRedisplay();

#undef JT_INCR
}


void myIdle(void)
//------------------------------------------------------------------------------
// Compute new positions for the particle system.
//
// GLUT myIdle() Callback, called when OS has nothing to do; a 'clock tick'.
// Use 'myIdle' *ONLY IF* your program does anything that needs continual
// updates, even when users are not pressing keys, then put code to do the
// updates here.  If you need to redraw the screen after your update, remember
// to call glutPostRedisplay() too.
//
//			*** AVOID THIS COMMON MISTAKE: ***
// If registered, 'myIdle()' gets called VERY OFTEN.  If you register myIdle()
// and leave that function empty, GLUT will waste most/all CPU time not
// otherwise occupied on very rapid and useless calls to idle().
//          *** ANIMATION STRATEGY ***
// --register myIdle() function at the end of myDisplay() callback;
// --inside the myIdle() function, compute new positions for onscreen objects,
// --at the end of the myIdle() function, UN-REGISTER the function so it won't
//      get called until we finish displaying the new positions.  Call
//      glutPostRedisplay() to trigger a call to myDisplay() callback function.
// --REPEAT.
{
//    cout << "..myIdle().." << endl;

    mySys.solver();             // Find the next state s0 of the particle system
                                // using the selected solver (e.g. Euler,
                                // mid-point, Runga-Kutta, implicit/back-wind),
                                // as set by mySys.solverType). Computes new
                                // state in mySys.s1, then swaps s1,s0 contents.

    glutIdleFunc(NULL);         // Done!  Now Un-register the idle fcn. so we
                                // won't compute again (until after we complete
                                // the myDisplay() function, where we again
                                // re-register it).
    glutPostRedisplay();        // trigger display() callback.
}
